/**
 * 
 */
package test.temp.suanfa;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;

import org.ehcache.impl.internal.concurrent.ConcurrentHashMap;
import org.junit.validator.PublicClassValidator;

import hyl.core.MyFun;
import hyl.core.run.IDo1;
import hyl.core.run.IDoEach1;
import hyl.core.run.IFilter;
import hyl.core.struct.MyNode;

/**
 * 
 * 
 * 2022年9月26日
 * 
 */

public class UserInfo extends MyNode<Integer, String> {
	// 关键用户编号
	public static int 管理员id = 1;
	public static int 平台ID = 2;
	static final int MAX抢单分组 = 7;

	public static Map<Integer, List<UserInfo>> 抢单用户分组 = new ConcurrentHashMap<>();

	public static Map<Integer, UserInfo> 所有用户 = new ConcurrentHashMap<>();
	public static Map<Integer, UserInfo> 临时用户集合 = new ConcurrentHashMap<>();// 用来挂载用户树的临时
	public static UserInfo _根节点 = new UserInfo(0, null);

	static {
		所有用户.put(0, _根节点);
		for (int i = 0; i < MAX抢单分组; i++) {
			抢单用户分组.put(i, new ArrayList<>());
		}
	}

	/**
	 * @param 编号
	 */
	public UserInfo(Integer 编号) {
		super(编号);
	}

	public UserInfo(Integer 编号, Integer 推荐人) {
		super(编号);
		if (推荐人 == null)
			return;
		UserInfo tjr = UserInfo.getUser(推荐人);
		if (tjr == null)
			return;
		this.set父节点(tjr);

	}

	public void remove() {
		super.remove();
		更新所有上级节点的伞下人数();
	}

	public void set父节点(UserInfo 父节点) {
		super.set父节点(父节点);
		更新所有上级节点的伞下人数();
	}

	void 更新所有上级节点的伞下人数() {
		int 商户标记 = (this.i系统角色 == 2) ? 1 : 0;
		int vip标记 = (this.i商城角色 > 0) ? 1 : 0;
		UserInfo 推荐人 = (UserInfo) this._nd父节点;
		if (推荐人 != null) {
			推荐人.i直推vip数 += vip标记;
			推荐人.i直推vip数 += 商户标记;
			this.up((nd) -> {
				if (nd == this)
					return;
				UserInfo uiInfo = (UserInfo) nd;
				uiInfo.i伞下人数++;
				uiInfo.i伞下vip数 += vip标记;
				uiInfo.i伞下商家数 += 商户标记;

			});
		}
	}

	public static void 刷新所有用户伞下人数() {
		_根节点.down((nd) -> {
			UserInfo u = (UserInfo) nd;
			u.i伞下人数 = 0;
			u.i伞下vip数 = 0;
			u.i伞下商家数 = 0;
			u.i直推vip数 = 0;
			u.i直推商家数 = 0;
		}, null);

		_根节点.down((nd) -> {
			UserInfo u = (UserInfo) nd;
			u.更新所有上级节点的伞下人数();
		}, null);

	}

	public static UserInfo getUser(Integer 编号) {
		UserInfo u = 所有用户.get(编号);
		return u;
	}

	public static UserInfo getUser(Integer 编号, Integer 推荐人) {
		UserInfo u = 所有用户.get(编号);
		if (u == null) {
			u = new UserInfo(编号, 推荐人);
			所有用户.put(编号, u);

		}
		return u;
	}

	public String s用户名 = ""; // user.uname
	public String s手机 = ""; // user.uphone
	public String s别名 = ""; // user.ualias
	// public int i推荐人 = 0;// n叉数的推荐关系 user.trjid
	// public HashSet<UserInfo> ls推荐子节点 = new HashSet<UserInfo>();// n叉数的推荐关系
	// user.trjid
	// public int i人数 = 0; // user_ext.i4
	public int c审核状态 = 0; // user.state 位1
	// public int i类型 = 0;// 转分红级别 ：user_ext.i3 //战区
	public String s注册时间 = "";
	public int i机构 = 0;
	// public int i角色 = 0;
	public int i购买 = 0;

	public String _name;
	public long l注册时间;
	public int i伞下人数;
	public int i伞下vip数;
	public int i伞下商家数;
	public int i直推vip数;
	public int i直推商家数;

	//////// 抢单////////
	public long i抢单日业绩;
	public long l抢单日团队业绩;
	// public long l抢单日团队手续费; 不能这么算 从下往上每层的比例不同哦
	public long l抢单日手续费;

	public int i当日抢单次数;
	public int i子节点数量;
	public int i临时1 = 0;
	// public int I抢单2次以上人数 = 0;
	public HashSet<UserInfo> nd子节点中抢单两次人员 = new HashSet<>();

	///////////// 商城
	public long l日团队云值;// 伞下累计奖励或附赠云值
	public long l日赠云值;
	public long l日增业绩;// 销售商品的价值日累计
	public long l日团队业绩;// 伞下销售商品的价值日累计
	public long l日打赏仓金;

	public long l日扣云值 = 0;
	public long l日扣仓金 = 0;
	// long l当日奖赠云值;
	public long l日伞下打赏仓金;

	public void f抢单结算前重置() {
		i抢单角色 = 0;
		l抢单日团队业绩 = 0;
		i抢单日业绩 = 0;
		l抢单日手续费 = 0;
		i当日抢单次数 = 0;
		i子节点数量 = 0;

		l无奖金手续费账户 = 0;
		i临时1 = 0;
		nd子节点中抢单两次人员.clear();

		// I抢单2次以上人数 = 0;
	}

	public void f商城结算前重置() {

		l日赠云值 = 0;
		l日增业绩 = 0;
		l日团队云值 = 0;
		l日团队业绩 = 0;
		l日打赏仓金 = 0;
		l日伞下打赏仓金 = 0;
		l日扣云值 = 0;

	}

	///////////// 商城分红/////
	public long get累计云值() {
		return l日赠云值 + this.l累计到昨日云值 - this.l日扣云值;
	}

	public long get累计仓金() {
		return l日打赏仓金 + this.l累计到昨日仓金 - this.l日扣仓金;
	}

	/// 角色
	public static final int D直营商家 = 1;
	public static final int D战区 = 2;
	public static final int D运营中心 = 4;
	public static final int D联合创始人 = 8;
	// public static final int D平台_公司 = 128; //id=2

	// 一个人可以有多个级别
	public static final String get级别(int 级别) {
		StringBuilder s = new StringBuilder();
		if ((级别 & 1) == 1)
			s.append(",直营商家");
		if ((级别 & 2) == 2)
			s.append(",直营战区");
		if ((级别 & 4) == 4)
			s.append(",运营中心");
		if ((级别 & 8) == 8)
			s.append(",联合创始人");

		return s.substring(1);
	}

	public int i系统角色 = 0; // 管理员=1 商家=2 用户=3
	public int i商城角色 = 0;//
	public int i分红级别 = 0;// 直营商家,战区,运营中心,联合创始人
	public int i商户代理角色 = 0; // 省市县代理
	public int i抢单角色 = 0;// 竞拍系统专用
	/////// 账户
	public long i兑换账户; ///
	public long l累计收益; //累计 止拍时抵扣掉
	public long l质押账户; //用于释放时 抵扣

	public long l无奖金手续费账户; //原来叫手续费2 这个账户手续费 用来拍卖拍品,但是没有奖金
	public long l仓金账户 = 0;// 用户仓金账户
	public long l余额账户 = 0;// 余额总账户,购买商品
	public long l竞拍余额账户 = 0; // 抢单账户,手续费1 的来源
	//public long l有奖金手续费账户;// 直接从总余额账户扣
	// 当日结算完成后拍照数据
	public long l累计到昨日云值 = 0;// 计算结束后更新,第二天结酬之前不变
	public long l累计到昨日业绩 = 0;// 计算结束后更新,第二天结酬之前不变
	public long l累计到昨日仓金 = 0;// 计算结束后更新,第二天结酬之前不变

	public int get抢单直推角色人数(int 抢单角色) {
		int i = 0;
		for (UserInfo ui : nd子节点中抢单两次人员) {
			if (ui.i抢单角色 == 抢单角色) {
				i++;
			}
		}
		return i;
	}

	public int get抢单达标团队数(int 抢单角色, int 每个团队至少几人) {
		AtomicInteger i = new AtomicInteger(0);

		int ii = 0;// 满足条件的团队数
		IFilter<MyNode<Integer, String>> fi1 = new IFilter<>() {
			@Override
			public boolean where(MyNode<Integer, String> nd) {
				UserInfo u = (UserInfo) nd;
				if (u.i抢单角色 == 抢单角色) {
					if (i.incrementAndGet() > 每个团队至少几人) {
						return true;
					}
				}
				return false;
			}
		};
		for (MyNode<Integer, String> ui : this._nd子节点) {
			i.set(0);
			ui.downUntil(fi1, null);
			if (i.get() > 每个团队至少几人) {
				ii++;
			}
		}
		return ii;
	}

	public int get伞下某商城角色数(int x商城角色) {
		AtomicInteger i = new AtomicInteger(0);
		down(nd -> {
			if (nd == this)
				return;
			UserInfo u = (UserInfo) nd;
			if (u.i商城角色 >= x商城角色)
				i.incrementAndGet();
		}, null);
		return i.get();
	}

	public boolean 至少n个直推用户旗下各m名x角色会员(int 至少n人, int 至少m名, int x角色) {
		int i = 0;
		for (MyNode<Integer, String> nd : _nd子节点) {
			UserInfo u = (UserInfo) nd;
			int ii = u.get伞下某商城角色数(x角色);
			if (ii >= 至少m名)
				i++;
			if (i > 至少n人)
				return true;
		}
		return false;

	}

	
	public static void 记平台收益(int 币种, Long 数额, int 科目, UserInfo 来源, String 备注) {
		long 时间戳 = MyFun.getMs();
		String msg = MyFun.join2("\t", 时间戳, UserInfo.平台ID, 来源._id, 科目, 币种, 数额, 备注);
		System.out.println(msg);

		if (币种 == 1)// 可奖金手续费
		{

		} else if (币种 == 2) {// 仓金手续费

		}

	}
	public static void 记平台销毁池(int 币种, Long 数额, int 科目, UserInfo 来源, String 备注) {
		long 时间戳 = MyFun.getMs();
		String msg = MyFun.join2("\t", 时间戳, UserInfo.平台ID, 来源._id, 科目, 币种, 数额, 备注);
		System.out.println(msg);

		if (币种 == 1)// 可奖金手续费
		{

		} else if (币种 == 2) {// 仓金手续费

		}

	}
	//??? 同步到数据库
	public void 记不产生奖金手续费(long 金额,Long 时间戳, String 备注) {
		l无奖金手续费账户+=金额;
		
	}
	//??? 同步到数据库
	public void 记累计收益(long 收益,Long 时间戳, String 备注) {
		l累计收益+=收益;
		
	}
	//??? 同步到数据库
	public void 记抢单静态收益(long 金额, T抢单 抢单) {
		///////////// 插入数据库记录
		// 插入数据库
		long 实际收益 = 金额 * (D参.比例1000 - D参.静态收益转兑换) / D参.比例1000;
		long 兑换金额 = 金额 * D参.静态收益转兑换 / D参.比例1000;
		i兑换账户 += 兑换金额;
		l累计收益+=实际收益;
		String msg = MyFun.join("{金额:", 金额, ",实际收益:", 实际收益, ",ID:", this._id, ",科目:", D参.D科目_竞拍_溢价收益, "}");

		// 奖金科目1 收取25%到账手续费 ,其实是25%转兑换产品账户
		System.out.println(msg);
	}
	//??? 同步到数据库
	public void 记抢单动态收益(long 金额, int 手续费类型, int 科目, String 备注) {
		///////////// 插入数据库记录
		// 插入数据库
		long 实际收益 = 金额 * (D参.比例1000 - D参.动态收益转兑换) / D参.比例1000;
		long 兑换金额 = 金额 * D参.动态收益转兑换 / D参.比例1000;
		// String msg = MyFun.join("{金额:", 金额, "兑换金额:", 兑换金额, ",兑换后收益:", 实际收益, ",ID:",
		// this._id, ",科目:", 科目, ",备注:", 备注,"}");
		String msg = MyFun.join2("\t","无奖金", this._id, 科目, 金额, 兑换金额, 实际收益, 备注);
		if (手续费类型 == 1) {
			l累计收益+=金额;
			i兑换账户 += 兑换金额;
			 msg = MyFun.join2("\t","奖金", this._id, 科目, 金额, 兑换金额, 实际收益, 备注);
		} else if (手续费类型 == 2) {
			l累计收益+=金额;
			i兑换账户 += 兑换金额;
			 msg = MyFun.join2("\t","仓金", this._id, 科目, 金额, 兑换金额, 实际收益, 备注);
		}
		System.out.println(msg);
	}
	//??? 同步到数据库
	public void 记流拍账(long 金额, int 科目, String 备注) {
		///////////// 插入数据库记录
		String msg = MyFun.join("{金额:", 金额, ",ID:", this._id, ",科目:", 科目, ",备注:", 备注, "}");

		System.out.println(msg);
	}
	//??? 同步到数据库
	public void 记用户商城账(long 单号, long 金额, int 科目, String 备注) {
		///////////// 插入数据库记录
		String msg = MyFun.join("{单号:", 单号, ",金额:", 金额, ",ID:", this._id, ",科目:", 科目, ",备注:", 备注, "}");
		l余额账户+=金额;
		System.out.println(msg);
	}

	public void 记用户云值(long 单号, long 云值, int 科目, String 备注) {
		///////////// 插入数据库记录
		String msg = MyFun.join("{单号:", 单号, ",云值:", 云值, ",ID:", this._id, ",角色:", this.i商城角色, ",科目:", 科目, ",备注:", 备注,
				"}");
		this.l日赠云值+=云值;
		System.out.println(msg);
	}

	public long get可打赏仓金(long 打赏仓金) {
		long 抵消仓金 = this.get累计云值() / D参.I仓金兑换价;
		if (抵消仓金 <= 0)
			return 0;
		if (抵消仓金 >= 打赏仓金) {
			return 打赏仓金;
		} else {
			return 0;
		}

	}

	public void 扣用户仓金(long 单号, long 仓金, int 科目, String 备注) {
		///////////// 插入数据库记录
		l仓金账户 -= 仓金;
		String msg = MyFun.join("{单号:", 单号, ",仓金:", 仓金, ",ID:", this._id, ",科目:", 科目, ",备注:", 备注, "}");
		System.out.println(msg);
	}

	/**
	 * 奖金获得人的情况
	 * 
	 * @param 单号
	 * @param 仓金
	 * @param 科目
	 * @param 备注
	 */
	public void 奖励仓金扣云值(long 单号, long 仓金, int 科目, String 备注) {
		if (仓金 == 0)
			return;
		long 抵消云值 = 仓金 * D参.I仓金兑换价;
		l日扣云值 += 抵消云值;
		l仓金账户 += 仓金;
		String msg = MyFun.join2("\t", "单号", "仓金", "抵扣云值", "UserID", "科目", "备注");
		// System.out.println(msg);

		///////////// 插入数据库记录
		msg = MyFun.join2("\t", 单号, 仓金, 抵消云值, this._id, 科目, 备注);
		System.out.println(msg);

	}

	public void 奖用户仓金不扣云值(long 单号, long 仓金, int 科目, String 备注) {
		///////////// 插入数据库记录
		l仓金账户 += 仓金;
		String msg = MyFun.join("{单号:", 单号, ",仓金:", 仓金, ",ID:", this._id, ",科目:", 科目, ",备注:", 备注, "}");
		System.out.println(msg);
	}

	public void 记用户现金奖(long 单号, long 现金, int 科目, String 备注) {
		l余额账户+=现金;
		///////////// 插入数据库记录
		String msg = MyFun.join("{单号:", 单号, ",现金:", 现金, ",ID:", this._id, ",科目:", 科目, ",备注:", 备注, "}");
		System.out.println(msg);
	}

	public void 设置商城角色(int 商城角色) {
		i商城角色 = 商城角色;
		// 写入数据对应字段

	}
	// 删除自身节点 要求必须是叶子节点才可以删除

	@SuppressWarnings("unlikely-arg-type")
	public boolean del() {
		if (this._nd子节点.isEmpty()) {
			super.remove();
			所有用户.remove(_id);
			for (int i = 0; i < MAX抢单分组; i++) {
				抢单用户分组.remove(this);
			}
			return true;
		}

		return false;
	}

	public static void clearAll() {
		_根节点.down(null, (nd) -> {
			if (nd == null)
				return;
			nd.remove();
		});
		所有用户.clear();
		所有用户.put(0, _根节点);
		临时用户集合.clear();
		for (int i = 0; i < MAX抢单分组; i++) {
			抢单用户分组.get(i).clear();
		}
		抢单用户分组.clear();
	}

	public void print() {
		String ms = MyFun.join("id:", _id, ",推荐人:", _id父节点, //
				",qd日手续费:", l抢单日手续费, //
				",i子节点数量:", i子节点数量, //
				",qd子节点数:", nd子节点中抢单两次人员.size(), ",临时:", i临时1);
		System.out.println(ms);
	}

	public void print抢单属性() {
		String ms = MyFun.join("id:", _id, ",推荐人:", _id父节点, //
				",i抢单角色:", i抢单角色, ",qd团队业绩:", l抢单日团队业绩, ",qd个人业绩:", i抢单日业绩, //
				",qd日手续费:", l抢单日手续费, //
				",qd次数:", i当日抢单次数, //
				",i子节点数量:", i子节点数量, //
				",qd子节点数:", nd子节点中抢单两次人员.size(), ",临时:", i临时1);
		System.out.println(ms);
	}

	public void print商城属性() {
		String ms = MyFun.join("id:", _id, ",推荐人:", _id父节点, //
				",i商城角色:", i商城角色, ",日团队云值:", l日团队云值, //
				",日赠云值:", l日赠云值, //
				",l日业绩:", l日增业绩, //
				",l日团队业绩:", l日团队业绩, //
				",l日打赏仓金:", l日打赏仓金, ",l日伞下打赏仓金:", l日伞下打赏仓金, ",临时:", i临时1);
		System.out.println(ms);
	}

	public void print存单属性() {
		String ms = MyFun.join("id:", _id, ",推荐人:", _id父节点, //
				",i商城角色:", i商城角色, ",质押账户:", this.l质押账户, //
				",无奖金手续费账户:", this.l无奖金手续费账户, //
				",余额账户:", this.l余额账户, //
				",竞拍余额账户:", this.l竞拍余额账户 //
				);
		System.out.println(ms);
	}
	public static void showAll() {
		show集合(所有用户.values());
	}

	public void showDown() {
		this.down((nd) -> {
			((UserInfo) nd).print();
		}, null);
	}

	public static void show叶集合() {
		show集合(叶节点集);
	}

	public static void show集合(Collection<UserInfo> 用户集) {
		for (UserInfo us : 用户集) {
			us.print();
		}
	}

	static ArrayList<List<UserInfo>> 各层节点 = new ArrayList<>();
	static List<UserInfo> 叶节点集 = new ArrayList<>();

	public static void f预遍历() {
		各层节点 = null;
		各层节点 = new ArrayList<>();
		各层节点.add(Arrays.asList(_根节点));
		叶节点集.clear();
		for (int i = 0; i < MAX抢单分组; i++) {
			抢单用户分组.put(i, new ArrayList<>());
		}
		// 层标记
		_根节点.down2(0L, (i, nd) -> {
			// level.get(i)
			if (nd == null)
				return true;

			// 如果层不够就扩展
			for (int j = 各层节点.size(); j < i + 1; j++) {
				各层节点.add(new ArrayList<>());
			}
			各层节点.get((int) i).add((UserInfo) nd);
			return false;
		}, null);
		// 标记所有叶节点
		_根节点.down2(0L, (i, nd) -> {
			if (nd._nd子节点.isEmpty())
				叶节点集.add((UserInfo) nd);
			return false;
		}, null);

	}

	// 不能这么写有隐藏bug
//	public static void f从节点集合向上遍历(Collection<UserInfo> 节点集,IDo1<MyNode<Integer, String>> worker) {
//		for (UserInfo u : 节点集) {
//			u.up(worker);
//		}
//	}
	public static void f从叶节点向上遍历(IDo1<MyNode<Integer, String>> worker) {
		for (UserInfo u : 叶节点集) {
			u.up(worker);
		}
	}

	public static void f遍历叶节点集(IDo1<MyNode<Integer, String>> worker) {
		for (UserInfo u : 叶节点集) {
			// u.up(worker);
			worker.run(u);
		}
	}

	public static void f广度遍历向下(IDoEach1<UserInfo> worker) {
		int size = 各层节点.size();
		for (AtomicInteger i = new AtomicInteger(1); i.get() < size; i.incrementAndGet()) {
			各层节点.get(i.get()).forEach((nd) -> {
				if (worker != null) {
					worker.isOver(i.get(), nd);
				}
			});
		}
	}

	public static void f广度遍历向上(IDoEach1<UserInfo> worker) {
		int size = 各层节点.size();
		for (AtomicInteger i = new AtomicInteger(size - 1); i.get() > 0; i.decrementAndGet()) {
			各层节点.get(i.get()).forEach((nd) -> {
				if (worker != null) {
					worker.isOver(i.get(), nd);
				}
			});
		}
	}
	// ----------------------------

}
